/**
 * 
 */
package merisis.impl;

import java.util.Iterator;
import java.util.logging.Logger;

import merisis.util.BitSet;
import merisis.util.BitSetIterator;
import merisis.util.BitUtil;
import merisis.util.OpenBitSetImpl;

import com.google.inject.Inject;
import com.google.inject.Injector;

public class Silo {

	@Inject
	private Hippocampus hippocampus;
	
	@Inject
	private Injector injector;
	
	@Inject
	private Logger logger;
	
	private SiloDisk surface;
	
	private BitSet ports = new OpenBitSetImpl();

	BitSet addedSPorts;
	
	private long root;
	
	private Siloyard yard;
	
	public Silo(Siloyard yard, long root) {
		
		this.root = root;
		this.yard = yard;
	}
	
	public long getTopVertex() {
		
		return root;
	}
	
	public BitSet getPorts() {
		
		return ports;
	}
	
	private void push(long[] s) {
		
		if (s == null)
			return;
		
		if (surface == null) {
			surface = new SiloDisk(yard, null, this);
			injector.injectMembers(surface);
		}

		surface.add(BitUtil.toBitSet(s));
		
	}
	
	private void refresh() {
		
		long[] limits = _limit(0);
		if (limits == null)
			refresh(new long[] {root});
		else 
			refresh(limits);
	}
	
	private void refresh(long[] vertexes) {
		
		for (int i = 0; i < vertexes.length; i ++) {
			
			long v = vertexes[i];
			long[] r = hippocampus.getSuccedents(v);
			if (r == null)
				continue;
			
			push(r);
			
			refresh(r);
		}
	}
	
	public Iterator<SiloDisk> iterator() {
		
		return new SpindleLayerIterator(surface);
	}
	
	public long[] limit(long s) {
		
		refresh();
		
		addedSPorts = new OpenBitSetImpl();
		
		long[] r = _limit(s);
		
		yard.registerPort(root, addedSPorts);
		ports.or(addedSPorts);
		addedSPorts = null;
		
		return r;
	}
	
	public void connectPort(long vertex, BitSet ontos, long root) {

		Iterator<SiloDisk> itr = iterator();
		while(itr.hasNext()) {
			
			SiloDisk disk = itr.next();
			if (disk.vertexes.get(vertex)) {
				
				BitSet ontos2 = disk.ontosMap.get(vertex);
				logger.finest("ver: " + vertex);
				BitSetIterator mItr = ontos.iterator();
				while(mItr.hasNext()) {
					long onto = mItr.next();
					if (ontos2.get(onto)) {
						ontos.clear(onto);
						ontos.set(root);
					}
				}
				break;
			}
		}
	}
	
	private long[] _limit(long s) {
		
		if (s == 0) {
			Iterator<SiloDisk> itr = iterator();
			if (!itr.hasNext())
				return null;

			SiloDisk layer = itr.next();
			
			return layer.getVertexes().toLongArray();
		}
		
		long[] r = null;
		Iterator<SiloDisk> itr = iterator();
		while(itr.hasNext()) {
			
			SiloDisk layer = itr.next();
			BitSet bs = layer.getAntecedents(s);
			if (bs == null)
				continue;
			
			r = bs.toLongArray();
			break;
		}
		
		return r;
	}
}